home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Environments / PowerMacOberon feb96 / Source / TextPFrames.Mod (.txt) < prev    next >
Encoding:
Oberon Text  |  1996-01-29  |  51.6 KB  |  1,100 lines  |  [TEXT/.Ob4]

  1. Syntax10.Scn.Fnt
  2. Syntax10b.Scn.Fnt
  3. Syntax10i.Scn.Fnt
  4. Math12.Scn.Fnt
  5. Syntax12.Scn.Fnt
  6. MODULE TextPFrames;    (** CAS 18-Jun-92 / MH 23 May 1993 / JT 07.10.93  (Rel. 2.43) **)
  7.     IMPORT
  8.         Modules, Input, Display, Fonts, Viewers, Oberon, MenuViewers, Texts, TextFrames, TextPrinter, Macintosh;
  9.     CONST
  10.         mm = TextFrames.mm; Scale = mm DIV 10;
  11.         unit = TextFrames.Unit; Unit = TextPrinter.Unit;
  12.         gridAdj = 0;  leftAdj = 1;  rightAdj = 2;  pageBreak = 3;
  13.         AdjMask = {leftAdj, rightAdj};
  14.         TAB = 9X; LF = 0AX; CR = 0DX; DEL = 7FX; BRK = 0ACX; ShiftBRK = 0ADX; CRSL = 0C4X; CRSR = 0C3X;
  15.         AdjustSpan = 30; MinTabWidth = 1 * Scale; StdTabWidth = 4 * mm;
  16.         rightKey = 0; middleKey = 1; leftKey = 2; cancel = {rightKey, middleKey, leftKey};
  17.     TYPE
  18.         TextLine = POINTER TO TextLineDesc;
  19.         Location* = RECORD
  20.             org*, pos*: LONGINT;
  21.             x*, y*, dx*, dy*: INTEGER;
  22.             line: TextLine;
  23.             trunc: BOOLEAN
  24.         END;
  25.         TextLineDesc = RECORD
  26.             next: TextLine;
  27.             eot: BOOLEAN;    (*contains end of text; first line after page break*)
  28.             indent: LONGINT;    (* first line indentation in units *)
  29.             pno: INTEGER;    (*3 0: page number of page containing first line after page break*)
  30.             w, h, dsr: INTEGER;    (**bounding box clipped to frame*)
  31.             nob: INTEGER;    (*number of contained blanks; > 0 if text line wraps around*)
  32.             org, len, span: LONGINT;    (*len w/o; span w/ trailing CR or white space, if any*)
  33.             P: TextFrames.Parc;
  34.             pbeg: LONGINT
  35.         END;
  36.         Frame* = POINTER TO FrameDesc;
  37.         FrameDesc* = RECORD (TextFrames.FrameDesc)
  38.             CarLoc*, SelBeg*, SelEnd*: Location;
  39.             trailer: TextLine;    (*ring with trailer and header*)
  40.             pages, first, width: INTEGER;    (*nof pages; if > 0: no of first page, print body width in print units*)
  41.             porg: ARRAY 1024 OF LONGINT
  42.         END;
  43.         SelectMsg = RECORD (Display.FrameMsg)
  44.             text: Texts.Text;
  45.             beg, end: LONGINT;
  46.             time: LONGINT
  47.         END;
  48.         pfnt: Fonts.Font;
  49.         (*shared globals => get rid off in a later version?*)
  50.         W: Texts.Writer;
  51.         WL: Texts.Writer;
  52.         PB: Texts.Buffer;
  53.         B: Texts.Buffer;
  54.         P: TextFrames.Parc;
  55.         pbeg: LONGINT;    (*inv Pos(P) = pbeg*)
  56.         R: Texts.Reader;
  57.         nextCh: CHAR;    (*inv Base(R) = T => T[Pos(R)-1] = nextCh]*)
  58.     PROCEDURE Min (x, y: INTEGER): INTEGER;
  59.     BEGIN
  60.         IF x < y THEN RETURN x ELSE RETURN y END
  61.     END Min;
  62.     PROCEDURE Max (x, y: INTEGER): INTEGER;
  63.     BEGIN
  64.         IF x > y THEN RETURN x ELSE RETURN y END
  65.     END Max;
  66.     PROCEDURE PU (x: INTEGER): INTEGER;    (*screen to printer space*)
  67.     BEGIN RETURN SHORT((x * LONG(unit) + Unit DIV 2) DIV Unit)
  68.     END PU;
  69.     PROCEDURE SU (x: INTEGER): INTEGER;    (*printer to screen space*)
  70.     BEGIN RETURN SHORT((x * LONG(Unit) + unit DIV 2) DIV unit)
  71.     END SU;
  72.     PROCEDURE MarkMenu (F: Frame);
  73.         VAR R: Texts.Reader; V: Viewers.Viewer; T: Texts.Text; ch: CHAR;
  74.     BEGIN V := Viewers.This(F.X, F.Y);
  75.         IF (V IS MenuViewers.Viewer) & (V.dsc IS TextFrames.Frame) THEN
  76.             T := V.dsc(TextFrames.Frame).text;
  77.             IF T.len > 0 THEN Texts.OpenReader(R, T, T.len - 1); Texts.Read(R, ch) ELSE ch := 0X END;
  78.             IF ch # "!" THEN Texts.Write(WL, "!"); Texts.Append(T, WL.buf) END
  79.         END
  80.     END MarkMenu;
  81.     (* Element Subframes *)
  82.     PROCEDURE InvertBorder (F: Display.Frame);
  83.     BEGIN
  84.         Display.ReplPattern(Display.white, Display.grey1, F.X-1, F.Y-1, F.W+2, 1, Display.invert);
  85.         Display.ReplPattern(Display.white, Display.grey1, F.X-1, F.Y+F.H, F.W+2, 1, Display.invert);
  86.         Display.ReplPattern(Display.white, Display.grey1, F.X-1, F.Y, 1, F.H, Display.invert);
  87.         Display.ReplPattern(Display.white, Display.grey1, F.X+F.W, F.Y, 1, F.H, Display.invert)
  88.     END InvertBorder;
  89.     PROCEDURE InvalSubFrames (F: Frame; x, y, w, h: INTEGER);
  90.         VAR p, f: Display.Frame; msg: MenuViewers.ModifyMsg;
  91.     BEGIN
  92.         IF (w > 0) & (h > 0) THEN f := F.dsc;
  93.             IF f # NIL THEN p := f; f := p.next END;
  94.             WHILE f # NIL DO
  95.                 IF (f.X < x + w) & (f.X + f.W > x) & (f.Y < y + h) & (f.Y + f.H > y) THEN p.next := f.next;
  96.                     msg.id := MenuViewers.reduce; msg.dY := 0; msg.Y := f.Y; msg.H := 0;
  97.                     f.handle(f, msg)
  98.                 ELSE p := f
  99.                 END;
  100.                 f := p.next
  101.             END;
  102.             f := F.dsc;
  103.             IF (f # NIL) & (f.X < x + w) & (f.X + f.W > x) & (f.Y < y + h) & (f.Y + f.H > y) THEN F.dsc := F.dsc.next;
  104.                 msg.id := MenuViewers.reduce; msg.dY := 0; msg.Y := f.Y; msg.H := 0;
  105.                 f.handle(f, msg)
  106.             END
  107.         END
  108.     END InvalSubFrames;
  109.     PROCEDURE ShiftSubFrames (F: Frame; oldY, newY, h: INTEGER);
  110.         VAR f: Display.Frame; msg: MenuViewers.ModifyMsg;
  111.     BEGIN
  112.         IF oldY > newY THEN InvalSubFrames(F, F.X, newY, F.W, oldY - newY)
  113.         ELSE InvalSubFrames(F, F.X, oldY + h, F.W, newY - oldY)
  114.         END;
  115.         f := F.dsc;
  116.         WHILE f # NIL DO
  117.             IF (f.Y < oldY + h) & (f.Y + f.H > oldY) THEN INC(f.Y, newY - oldY);
  118.                 msg.id := MenuViewers.reduce; msg.dY := 0; msg.Y := f.Y; msg.H := f.H;
  119.                 f.handle(f, msg)
  120.             END;
  121.             f := f.next
  122.         END
  123.     END ShiftSubFrames;
  124.     (* Display Primitives *)
  125.     PROCEDURE DrawCursor (x, y: INTEGER);
  126.     BEGIN Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, x, y)
  127.     END DrawCursor;
  128.     PROCEDURE TrackMouse (VAR x, y: INTEGER; VAR keys, keysum: SET);
  129.     BEGIN Input.Mouse(keys, x, y); keysum := keysum + keys; DrawCursor(x, y)
  130.     END TrackMouse;
  131.     PROCEDURE EraseRect (F: Frame; x, y, w, h: INTEGER);
  132.     BEGIN Display.ReplConst(Display.black, x, y, w, h, Display.replace); InvalSubFrames(F, x, y, w, h)
  133.     END EraseRect;
  134.     PROCEDURE Erase (F: Frame; x, y, w, h: INTEGER);    (*RemoveMarks optimization*)
  135.     BEGIN
  136.         IF h > 0 THEN Oberon.RemoveMarks(x, y, w, h); EraseRect(F, x, y, w, h) END
  137.     END Erase;
  138.     PROCEDURE Shift (F: Frame; oldY, newY, h: INTEGER);    (*RemoveMarks optimization*)
  139.     BEGIN
  140.         IF (oldY # newY) & (h > 0) THEN
  141.             Oberon.RemoveMarks(F.X + F.left, Min(oldY, newY), F.W - F.left, Max(oldY, newY) + h);
  142.             Display.CopyBlock(F.X + F.left, oldY, F.W - F.left, h, F.X + F.left, newY, Display.replace);
  143.             ShiftSubFrames(F, oldY, newY, h)
  144.         END
  145.     END Shift;
  146.     PROCEDURE InvertCaret (F: Frame);
  147.         VAR loc: Location;
  148.     BEGIN loc := F.CarLoc; Display.CopyPattern(Display.white, Display.hook, loc.x, loc.y + loc.line.dsr - 6, Display.invert)
  149.     END InvertCaret;
  150.     PROCEDURE InvertRect (F: Frame; x, y, w, h: INTEGER);    (*clips to right and bottom frame margin*)
  151.     BEGIN
  152.         IF x + w > F.X + F.W - F.right THEN w := F.X + F.W - F.right - x END;
  153.         IF y >= F.Y + F.bot THEN Display.ReplConst(Display.white, x, y, w, h, Display.invert) END
  154.     END InvertRect;
  155.     PROCEDURE InvertSelection (F: Frame; beg, end: Location);
  156.         VAR t: TextLine; ex, rx, w, py: INTEGER;
  157.     BEGIN rx := F.X + F.W - F.right; t := end.line;
  158.         IF t.eot OR (end.pos <= t.org + t.len) THEN ex := end.x ELSE ex := rx END;
  159.         IF beg.line = end.line THEN InvertRect(F, beg.x, beg.y, ex - beg.x, beg.line.h)
  160.         ELSE t := beg.line; py := beg.y; w := F.W - F.left - F.right;
  161.             InvertRect(F, beg.x, py, rx - beg.x, t.h); t := t.next; DEC(py, t.h);
  162.             WHILE t # end.line DO InvertRect(F, F.X + F.left, py, w, t.h); t := t.next; DEC(py, t.h) END;
  163.             IF end.line.eot THEN InvertRect(F, F.X + F.left, py, end.x - (F.X + F.left), t.h)
  164.             ELSE InvertRect(F, F.X + F.left, py, ex - (F.X + F.left), t.h)
  165.             END
  166.         END
  167.     END InvertSelection;
  168.     PROCEDURE CoordToPos (F: Frame; mh: INTEGER): LONGINT;
  169.         VAR h: INTEGER;
  170.     BEGIN h := F.H - 1;
  171.         IF h > 0 THEN RETURN ((h - mh) * F.text.len + h DIV 2) DIV h ELSE RETURN 0 END
  172.     END CoordToPos;
  173.     PROCEDURE ShowBar (F: Frame; botH, topH: INTEGER);
  174.     BEGIN
  175.         IF (F.left > F.barW) & (F.barW > 0) THEN
  176.             Display.ReplConst(Display.white, F.X + F.barW - 1, F.Y + botH, 1, topH - botH, Display.replace)
  177.         END
  178.     END ShowBar;
  179.     PROCEDURE Tick (F: Frame);
  180.     BEGIN
  181.         IF (0 <= F.markH) & (F.markH < F.H) & (F.left > F.barW) & (F.barW > 6) & (F.H > 1) THEN
  182.             Display.ReplConst(Display.white, F.X + 1, F.Y + F.markH, F.barW - 6, 1, Display.invert)
  183.         END
  184.     END Tick;
  185.     PROCEDURE ShowTick (F: Frame);    (*removes global marks as needed*)
  186.         VAR h, mh: INTEGER; len: LONGINT;
  187.     BEGIN h := F.H - 1; len := F.text.len;
  188.         IF len > 0 THEN mh := SHORT(h - h * F.org DIV len) ELSE mh := h END;
  189.         IF F.markH # mh THEN Oberon.RemoveMarks(F.X, F.Y, F.barW, F.H);
  190.             Tick(F); F.markH := mh; Tick(F)
  191.         END
  192.     END ShowTick;
  193.     (** Pagination Support **)
  194.     PROCEDURE LocatePage* (F: Frame; org: LONGINT; VAR porg: LONGINT; VAR pno: INTEGER);
  195.         VAR i: INTEGER;
  196.     BEGIN i := 0;
  197.         WHILE (i < F.pages) & (F.porg[i] < org) DO INC(i) END;
  198.         IF i < F.pages THEN porg := F.porg[i]; pno := F.first + i
  199.         ELSE porg := F.text.len; pno := LEN(F.porg)
  200.         END
  201.     END LocatePage;
  202.     PROCEDURE GetPagination* (F: Frame; VAR pages, first, width: INTEGER; VAR porg: ARRAY OF LONGINT);
  203.         VAR i: INTEGER;
  204.     BEGIN pages := Min(F.pages, SHORT(LEN(porg))); first := F.first; width := F.width; i := pages;
  205.         WHILE i > 0 DO DEC(i); F.porg[i] := porg[i] END
  206.     END GetPagination;
  207.     PROCEDURE SetPagination* (F: Frame; pages, first, width: INTEGER; VAR porg: ARRAY OF LONGINT);
  208.     BEGIN pages := Min(pages, LEN(F.porg)); F.pages := pages; F.first := first; F.width := width;
  209.         WHILE pages > 0 DO DEC(pages); F.porg[pages] := porg[pages] END
  210.     END SetPagination;
  211.     (* Screen Metrics *)
  212.     PROCEDURE GetChar (fnt: Fonts.Font; ch: CHAR; VAR dx, x, y, w, h: INTEGER; VAR pat: Display.Pattern);
  213.         (*dx, x, w: printer space*)
  214.     BEGIN Display.GetChar(fnt.raster, ch, dx, x, y, w, h, pat);
  215.         dx := SHORT(TextPrinter.DX(TextPrinter.FontNo(fnt), ch) DIV Unit);
  216.         x := PU(x); w := PU(w)
  217.     END GetChar;
  218.     PROCEDURE Tab (dw: INTEGER; VAR dx: INTEGER);    (*P set*)
  219.         VAR i, n: INTEGER; w: LONGINT;
  220.     BEGIN i := 0; n := P.nofTabs; w := LONG(dw) * Unit + MinTabWidth;
  221.         IF dw < 0 THEN dx := -dw
  222.         ELSE
  223.             WHILE (i < n) & (P.tab[i] < w) DO INC(i) END;
  224.             IF i < n THEN dx := SHORT((P.tab[i] - LONG(dw) * Unit) DIV Unit)
  225.             ELSE dx := StdTabWidth DIV Unit
  226.             END
  227.         END
  228.     END Tab;
  229.     PROCEDURE MeasureSpecial (dw: INTEGER; VAR dx, x, y, w, h: INTEGER; VAR trunc: BOOLEAN);
  230.         (*P, R, nextCh set*)    (*dx, x, w: printer space*)
  231.         VAR e: Texts.Elem; pat: Display.Pattern; pw, ph: LONGINT;
  232.             msg: TextFrames.DisplayMsg; pmsg: TextPrinter.PrintMsg;
  233.     BEGIN
  234.         IF nextCh = " " THEN GetChar(R.fnt, nextCh, dx, x, y, w, h, pat);
  235.             x := 0; y := 0; w := dx; h := 0; trunc := FALSE
  236.         ELSIF nextCh = TAB THEN Tab(dw, dx); x := 0; y := 0; w := dx; h := 0; trunc := FALSE
  237.         ELSIF R.elem # NIL THEN e := R.elem;
  238.             pmsg.prepare := TRUE; pmsg.indent := LONG(dw) * Unit;
  239.             pmsg.fnt := R.fnt; pmsg.col := R.col; pmsg.pos := Texts.Pos(R)-1;
  240.             pmsg.Y0 := -SHORT(P.dsr DIV Unit);
  241.             e.handle(e, pmsg); pw := e.W; ph := e.H;
  242.             msg.prepare := TRUE; msg.indent := LONG(dw) * Unit;
  243.             msg.fnt := R.fnt; msg.col := R.col; msg.pos := Texts.Pos(R)-1;
  244.             msg.Y0 := -SHORT(P.dsr DIV unit);
  245.             e.handle(e, msg);
  246.             w := SHORT(pw DIV Unit); h := SHORT(ph DIV unit); dx := w; x := 0; y := msg.Y0;
  247.             trunc := ~(e IS TextFrames.Parc) & ((pw < e.W) OR (ph < e.H))
  248.         ELSE GetChar(R.fnt, nextCh, dx, x, y, w, h, pat); trunc := FALSE
  249.         END
  250.     END MeasureSpecial;
  251.     PROCEDURE GetSpecial (F: Frame; VAR n: INTEGER; cn, ddx, dw: INTEGER;
  252.             VAR dx, x, y, w, h: INTEGER; VAR trunc: BOOLEAN);
  253.         (*P, R, nextCh set*)    (*ddx, dw, dx, x, w: printer space*)
  254.         VAR e: Texts.Elem; pat: Display.Pattern;
  255.     BEGIN
  256.         IF nextCh = " " THEN GetChar(R.fnt, nextCh, dx, x, y, w, h, pat);
  257.             x := 0; y := 0; INC(dx, ddx); INC(n); IF n <= cn THEN INC(dx) END;    (*space correction for block adjustment*)
  258.             w := dx; h := 0; trunc := FALSE
  259.         ELSIF nextCh = TAB THEN Tab(dw, dx); x := 0; y := 0; w := dx; h := 0; trunc := FALSE
  260.         ELSIF R.elem # NIL THEN e := R.elem;
  261.             IF (e IS TextFrames.Parc) & (P.W = 9999 * Unit) THEN
  262.                 w := Min(SHORT((P.width + P.left) DIV Unit), PU(F.W - F.right - F.left));
  263.                 e.W := w * LONG(Unit); h := SHORT(e.H DIV unit); trunc := FALSE
  264.             ELSE MeasureSpecial(dw, dx, x, y, w, h, trunc)
  265.             END;
  266.             dx := w; x := 0; y := -SHORT(P.dsr DIV unit)
  267.         ELSE GetChar(R.fnt, nextCh, dx, x, y, w, h, pat); trunc := FALSE
  268.         END
  269.     END GetSpecial;
  270.     PROCEDURE NextLine (T: Texts.Text; VAR org: LONGINT);    (*R, nextCh set*)
  271.         VAR pat: Display.Pattern; pos, bk, d: LONGINT; width, tw, dx, x, y, w, h: INTEGER; trunc: BOOLEAN;
  272.             R1: Texts.Reader; peekCh: CHAR; indent: LONGINT;
  273.     BEGIN tw := 0; dx := 0; w := 0; bk := -999;    (*org = Texts.Pos(R)-1*)
  274.         pos := org; TextFrames.ParcBefore(T, pos, P, pbeg); width := SHORT(P.width DIV Unit);
  275.         indent := 0;
  276.         IF org > 0 THEN Texts.OpenReader(R1, T, org - 1); Texts.Read(R1, peekCh);
  277.             IF (peekCh = CR) OR (R1.elem # NIL) & (R1.elem IS TextFrames.Parc) THEN indent := P.first END;
  278.         END;
  279.         DEC(width, SHORT(indent DIV Unit));
  280.         LOOP INC(pos);    (*inv pos = Texts.Pos(R), ~R.eof => nextCh = text[pos-1]*)
  281.             IF R.eot OR (nextCh = CR) THEN EXIT END;
  282.             INC(tw, dx);
  283.             IF nextCh <= " " THEN MeasureSpecial(tw + SHORT(indent DIV Unit), dx, x, y, w, h, trunc)
  284.             ELSE GetChar(R.fnt, nextCh, dx, x, y, w, h, pat)
  285.             END;
  286.             IF tw + x + dx > width THEN d := pos - bk;
  287.                 IF (0 <= d) & (d < AdjustSpan) & (nextCh > " ") THEN pos := bk
  288.                 ELSIF ((nextCh > " ") OR (nextCh = Texts.ElemChar)) & (pos > org + 1) THEN DEC(pos)
  289.                 END;
  290.                 Texts.OpenReader(R, T, pos); Texts.Read(R, nextCh);
  291.                 EXIT
  292.             END;
  293.             IF (nextCh <= " ") & (nextCh # Texts.ElemChar) THEN bk := pos END;
  294.             Texts.Read(R, nextCh)
  295.         END;
  296.         org := pos
  297.     END NextLine;
  298.     PROCEDURE BegOfLine* (T: Texts.Text; VAR pos: LONGINT; adjust: BOOLEAN);
  299.         VAR p, org: LONGINT;
  300.     BEGIN
  301.         IF pos <= 0 THEN pos := 0
  302.         ELSE
  303.             IF pos <= T.len THEN org := pos ELSE org := T.len END;
  304.             LOOP    (*search backwards for CR*)
  305.                 IF org = 0 THEN EXIT END;
  306.                 Texts.OpenReader(R, T, org - 1); Texts.Read(R, nextCh);
  307.                 IF nextCh = CR THEN EXIT END;
  308.                 DEC(org)
  309.             END;
  310.             IF adjust THEN    (*search forward for actual line origin*)
  311.                 Texts.OpenReader(R, T, org); Texts.Read(R, nextCh); p := org;
  312.                 REPEAT org := p; NextLine(T, p) UNTIL (p > pos) OR R.eot
  313.             END;
  314.             pos := org
  315.         END
  316.     END BegOfLine;
  317.     PROCEDURE AdjustMetrics (F: Frame; t: TextLine; VAR pw, tw, ddx, cn: INTEGER);
  318.         (*t.org set*)    (*pw, tw, ddx, cn: printer space*)
  319.         VAR w: INTEGER;
  320.     BEGIN P := t.P; pbeg := t.pbeg;
  321.         pw := PU(F.left); tw := PU(t.w); ddx := 0; cn := 0;
  322.         IF t.pbeg # t.org THEN
  323.             INC(pw, SHORT((P.left + t.indent) DIV Unit));
  324.             DEC(tw, SHORT(t.indent DIV Unit));
  325.             IF leftAdj IN P.opts THEN
  326.                 IF (rightAdj IN P.opts) & (t.nob > 0) THEN
  327.                     w := tw; tw := SHORT((P.width - t.indent) DIV Unit);
  328.                     ddx := (tw - w) DIV t.nob; cn := (tw - w) MOD t.nob
  329.                 END
  330.             ELSIF rightAdj IN P.opts THEN INC(pw, SHORT(P.width DIV Unit) - tw)
  331.             ELSE (*center*) INC(pw, (SHORT(P.width DIV Unit) - tw) DIV 2)
  332.             END
  333.         END
  334.     END AdjustMetrics;
  335.     (* Screen Placement *)
  336.     PROCEDURE DrawSpecial (F: Frame; px, py, x, y: INTEGER);    (*R, nextCh set*)    (*px, x: printer space*)
  337.         VAR e: Texts.Elem; pat: Display.Pattern; dx, w, h: INTEGER;
  338.             msg: TextFrames.DisplayMsg;
  339.     BEGIN
  340.         IF (nextCh = " ") OR (nextCh = CR) OR (nextCh = TAB) THEN (*skip*)
  341.         ELSIF R.elem # NIL THEN e := R.elem;
  342.             IF ~(e IS TextFrames.Parc) OR F.showsParcs THEN
  343.                 msg.prepare := FALSE;
  344.                 msg.fnt := R.fnt; msg.col := R.col; msg.pos := Texts.Pos(R) - 1;
  345.                 msg.frame := F; msg.X0 := SU(px + x); msg.Y0 := py + y;
  346.                 msg.elemFrame := NIL;
  347.                 e.handle(e, msg);
  348.                 IF msg.elemFrame # NIL THEN msg.elemFrame.next := F.dsc; F.dsc := msg.elemFrame END
  349.             ELSIF (e IS TextFrames.Parc) & ~F.showsParcs & (pageBreak IN e(TextFrames.Parc).opts) THEN
  350.                 Display.ReplPattern(Display.white, Display.grey1, SU(px + x), py, SHORT(e.W DIV Unit), 1, Display.replace)
  351.             END
  352.         ELSE GetChar(R.fnt, nextCh, dx, x, y, w, h, pat);
  353.             Display.CopyPattern(R.col, pat, SU(px + x), py + y, Display.replace)
  354.         END;
  355.     END DrawSpecial;
  356.     PROCEDURE DrawBanner (F: Frame; pno, bw, px, py, th, mw: INTEGER);
  357.         VAR pat: Display.Pattern; i, j, dx, x, y, w, h: INTEGER; pstr: ARRAY 5 OF CHAR; ch: CHAR;
  358.     BEGIN
  359.         IF bw <= mw THEN
  360.             Display.ReplPattern(Display.white, Display.grey1, px + bw, py, 1, th, Display.replace);
  361.             DEC(bw, 2)
  362.         ELSE bw := mw
  363.         END;
  364.         INC(py, th - 18);
  365.         i := 0; j := pno;
  366.         REPEAT pstr[i] := CHR(30H + j MOD 10); j := j DIV 10; INC(i) UNTIL j = 0;
  367.         WHILE j < i DO ch := pstr[j]; Display.GetChar(pfnt.raster, ch, dx, x, y, w, h, pat); DEC(bw, dx); INC(j) END;
  368.         Display.ReplConst(Display.white, px, py, bw - 2, 1, Display.replace);
  369.         Display.ReplConst(Display.white, px, py + 2, bw - 2, 1, Display.replace);
  370.         INC(px, bw);
  371.         WHILE i > 0 DO DEC(i); ch := pstr[i]; Display.GetChar(pfnt.raster, ch, dx, x, y, w, h, pat);
  372.             Display.CopyPattern(Display.white, pat, px + x, py + y, Display.replace);
  373.             INC(px, dx)
  374.         END
  375.     END DrawBanner;
  376.     PROCEDURE ShowLine (F: Frame; t: TextLine; left, right, py: INTEGER);    (*left, right: printer space*)
  377.         VAR pat: Display.Pattern; i: LONGINT; trunc: BOOLEAN;
  378.             n, cn, lm, px, pw, tw, ddx, dx, x, y, w, h: INTEGER;
  379.     BEGIN Texts.OpenReader(R, F.text, t.org); AdjustMetrics(F, t, pw, tw, ddx, cn);
  380.         IF F.pages > 0 THEN
  381.             IF SU(F.width) < F.W - F.left - F.right THEN
  382.                 Display.ReplPattern(Display.white, Display.grey1, F.X + F.left + SU(F.width), py, 1, t.h, Display.replace)
  383.             END;
  384.             IF t.pno >= 0 THEN
  385.                 DrawBanner(F, t.pno, SU(F.width), F.X + F.left, py, t.h, F.W - F.left - F.right)
  386.             END
  387.         END;
  388.         lm := PU(F.X + F.left) + SHORT(P.left DIV Unit); px := PU(F.X) + pw; INC(py, t.dsr); i := 0; n := 0;
  389.         px := px + PU (F.X + F.left) - PU (F.X) - PU (F.left);    (* correct potential rounding error *)
  390.         WHILE i < t.len DO Texts.Read(R, nextCh);
  391.             IF nextCh <= " " THEN GetSpecial(F, n, cn, ddx, px - lm, dx, x, y, w, h, trunc)
  392.             ELSE GetChar(R.fnt, nextCh, dx, x, y, w, h, pat)
  393.             END;
  394.             INC(y, R.fnt.height * R.voff DIV 64);
  395.             IF px + x + w <= right THEN
  396.                 IF px  + x >= left THEN
  397.                     IF nextCh <= " " THEN
  398.                         IF trunc THEN
  399.                             Display.ReplPattern(R.col, Display.grey0, SU(px + x), py + y, SU(w), h, Display.replace)
  400.                         ELSE DrawSpecial(F, px, py, x, y)
  401.                         END
  402.                     ELSE Display.CopyPattern(R.col, pat, SU(px + x), py + y, Display.replace)
  403.                     END
  404.                 END;
  405.                 INC(px, dx); INC(i)
  406.             ELSE i := t.len
  407.             END
  408.         END
  409.     END ShowLine;
  410.     PROCEDURE ShowLines (F: Frame; botH, topH: INTEGER);
  411.         VAR t: TextLine; ph: INTEGER;
  412.     BEGIN t := F.trailer.next; ph := F.H - F.top;
  413.         WHILE (t # F.trailer) & (ph - t.h >= topH) DO DEC(ph, t.h); t := t.next END;
  414.         WHILE (t # F.trailer) & (ph - t.h >= botH) DO DEC(ph, t.h);
  415.             ShowLine(F, t, PU(F.X + F.left), PU(F.X + F.W - F.right), F.Y + ph); t := t.next
  416.         END
  417.     END ShowLines;
  418.     PROCEDURE ShowLinesErasing (F: Frame; botH, topH: INTEGER);
  419.         VAR t: TextLine; ph: INTEGER;
  420.     BEGIN t := F.trailer.next; ph := F.H - F.top;
  421.         WHILE (t # F.trailer) & (ph - t.h >= topH) DO DEC(ph, t.h); t := t.next END;
  422.         WHILE (t # F.trailer) & (ph - t.h >= botH) DO DEC(ph, t.h);
  423.             Erase(F, F.X + F.left, F.Y + ph, F.W - F.right - F.left, t.h);
  424.             ShowLine(F, t, PU(F.X + F.left), PU(F.X + F.W - F.right), F.Y + ph); t := t.next
  425.         END
  426.     END ShowLinesErasing;
  427.     (* Screen Casting *)
  428.     PROCEDURE MeasureLine (F: Frame; maxW: INTEGER; t: TextLine);    (*R, nextCh set*)
  429.         VAR pat: Display.Pattern; porg, len, bklen, d: LONGINT; eol, trunc: BOOLEAN;
  430.             nob, bknob, width, minY, bkminY, maxY, bkmaxY, tw, bktw, lsp, dsr, dx, x, y, w, h: INTEGER;
  431.             R1: Texts.Reader; peekCh: CHAR;
  432.     BEGIN
  433.         len := 0; nob := 0; bklen := -999; tw := 0; dx := 0; minY := 0; maxY := 0;
  434.         TextFrames.ParcBefore(F.text, t.org, P, pbeg);
  435.         lsp := SHORT(P.lsp DIV unit); dsr := SHORT(P.dsr DIV unit); width := SHORT(P.width DIV Unit);
  436.         t.indent := 0;
  437.         IF t.org > 0 THEN Texts.OpenReader(R1, F.text, t.org - 1); Texts.Read(R1, peekCh);
  438.             IF (peekCh = CR) OR (R1.elem # NIL) & (R1.elem IS TextFrames.Parc) THEN t.indent := P.first END;
  439.         END;
  440.         INC(tw, SHORT(t.indent DIV Unit));
  441.         LOOP
  442.             IF R.eot OR (nextCh = CR) THEN nob := 0; eol := ~R.eot; EXIT END;
  443.             IF nextCh <= " " THEN MeasureSpecial(tw, dx, x, y, w, h, trunc)
  444.             ELSE GetChar(R.fnt, nextCh, dx, x, y, w, h, pat)
  445.             END;
  446.             IF tw + dx > width THEN d := len - bklen;
  447.                 IF (d < AdjustSpan) & (nextCh > " ") THEN eol := TRUE;
  448.                     Texts.OpenReader(R, F.text, Texts.Pos(R) - d);
  449.                     nob := bknob; len := bklen; tw := bktw; minY := bkminY; maxY := bkmaxY
  450.                 ELSIF len = 0 THEN    (* force at least one character on each line *)
  451.                     INC(len); INC(y, R.fnt.height * R.voff DIV 64); minY := Min(minY, y); maxY := Max(maxY, y + h);
  452.                     Texts.Read(R, nextCh); eol := FALSE; tw := maxW
  453.                 ELSE eol := (nextCh <= " ") & (nextCh # Texts.ElemChar)
  454.                 END;
  455.                 EXIT
  456.             END;
  457.             IF (nextCh <= " ") & (nextCh # Texts.ElemChar) THEN
  458.                 bknob := nob; bklen := len; bktw := tw; bkminY := minY; bkmaxY := maxY;
  459.                 IF nextCh = " " THEN INC(nob) END
  460.             END;
  461.             INC(len); INC(tw, dx); INC(y, R.fnt.height * R.voff DIV 64);
  462.             IF y < minY THEN minY := y END;
  463.             IF y + h > maxY THEN maxY := y + h END;
  464.             Texts.Read(R, nextCh)
  465.         END;
  466.         IF ~F.showsParcs & (pbeg = t.org) THEN dsr := 0; t.h := SHORT(P.lead DIV unit) + 1
  467.         ELSIF gridAdj IN P.opts THEN
  468.             WHILE dsr < -minY DO INC(dsr, lsp) END;
  469.             t.h := Max(lsp, dsr + maxY); INC(t.h, (-t.h) MOD lsp)
  470.         ELSE dsr := Max(dsr, -minY); t.h := Max(lsp, dsr + maxY)
  471.         END;
  472.         LocatePage(F, t.org, porg, t.pno);
  473.         IF t.org = porg THEN INC(t.h, 20) ELSE t.pno := -1 END;
  474.         t.len := len; t.w := Min(SU(tw), maxW); t.dsr := dsr; t.nob := nob; t.eot := R.eot; t.P := P; t.pbeg := pbeg;
  475.         IF eol THEN Texts.Read(R, nextCh); t.span := len + 1 ELSE t.span := len END
  476.     END MeasureLine;
  477.     PROCEDURE MeasureLines (F: Frame; org: LONGINT; VAR trailer: TextLine);
  478.         VAR s, t: TextLine; ph: INTEGER;
  479.     BEGIN NEW(trailer); s := trailer;
  480.         Texts.OpenReader(R, F.text, org); Texts.Read(R, nextCh); ph := F.H - F.top;
  481.         LOOP NEW(t); t.org := org; MeasureLine(F, F.W - F.left - F.right, t);
  482.             IF ph - t.h < F.bot THEN EXIT END;
  483.             s.next := t; s := t; INC(org, s.span); DEC(ph, s.h);
  484.             IF R.eot THEN EXIT END
  485.         END;
  486.         s.next := trailer; trailer.eot := TRUE; trailer.org := org; trailer.len := 0; trailer.w := 0;
  487.         trailer.h := SHORT(TextFrames.defParc.lsp DIV unit); trailer.P := P; trailer.pbeg := pbeg
  488.     END MeasureLines;
  489.     (** Locators **)
  490.     PROCEDURE LocateLineTop (F: Frame; trailer: TextLine; org: LONGINT; VAR loc: Location);
  491.         VAR t: TextLine; ph: INTEGER;
  492.     BEGIN ph := F.H - F.top; t := trailer.next;
  493.         WHILE (t # trailer) & (t.org # org) DO DEC(ph, t.h); t := t.next END;
  494.         loc.org := org; loc.line := t; loc.y := F.Y + ph
  495.     END LocateLineTop;
  496.     PROCEDURE Width (F: Frame; t: TextLine; pos: LONGINT; VAR pw, dx, dy: INTEGER);    (*pw, dx: printer space*)
  497.         VAR pat: Display.Pattern; i: LONGINT; n, mw, lm, tw, ddx, cn, x, y, w, h: INTEGER; trunc: BOOLEAN;
  498.     BEGIN AdjustMetrics(F, t, pw, tw, ddx, cn); dy := 0; lm := PU(F.left) + SHORT(P.left DIV Unit);
  499.         IF t # F.trailer THEN Texts.OpenReader(R, F.text, t.org); Texts.Read(R, nextCh);
  500.             i := 0; n := 0; DEC(pos, t.org); dx := 0; mw := PU(F.W - F.right);
  501.             WHILE ~R.eot & (i < t.len) & (i <= pos) & (pw + dx <= mw) DO INC(i); INC(pw, dx);
  502.                 IF nextCh <= " " THEN GetSpecial(F, n, cn, ddx, pw - lm, dx, x, y, w, h, trunc)
  503.                 ELSE GetChar(R.fnt, nextCh, dx, x, y, w, h, pat)
  504.                 END;
  505.                 dy := R.fnt.height * R.voff DIV 64;
  506.                 Texts.Read(R, nextCh)
  507.             END;
  508.             IF (i <= pos) & (pw + dx <= mw) THEN INC(i); INC(pw, dx) END
  509.         ELSE dx := PU(4)
  510.         END
  511.     END Width;
  512.     PROCEDURE LocatePos* (F: Frame; pos: LONGINT; VAR loc: Location);
  513.         VAR t: TextLine; pw, dx, dy: INTEGER;
  514.     BEGIN
  515.         IF pos < F.org THEN pos := F.org; t := F.trailer.next
  516.         ELSIF pos < F.trailer.org THEN t := F.trailer;
  517.             WHILE (t.next # F.trailer) & (t.next.org <= pos) DO t := t.next END
  518.         ELSE pos := F.trailer.org; t := F.trailer.next;
  519.             WHILE ~t.eot DO t := t.next END
  520.         END;
  521.         Width(F, t, pos, pw, dx, dy); LocateLineTop(F, F.trailer, t.org, loc); DEC(loc.y, loc.line.h);
  522.         loc.org := t.org; loc.pos := pos; loc.x := F.X + SU(pw); loc.dx := SU(dx); loc.dy := dy;
  523.         loc.line := t; loc.trunc := FALSE
  524.     END LocatePos;
  525.     PROCEDURE LocateLine* (F: Frame; y: INTEGER; VAR loc: Location);
  526.         VAR t: TextLine; h, ph, pw, tw, ddx, cn: INTEGER;
  527.     BEGIN t := F.trailer.next; h := y - F.Y; ph := F.H - F.top - t.h;
  528.         WHILE ~t.eot & (ph - t.next.h >= F.bot) & (ph > h) DO t := t.next; DEC(ph, t.h) END;
  529.         AdjustMetrics(F, t, pw, tw, ddx, cn);
  530.         IF pw >= PU(F.X + F.W - F.right) THEN pw := PU(F.X + F.W - F.right - 4) END;
  531.         loc.org := t.org; loc.pos := loc.org;
  532.         loc.x := F.X + SU(pw); loc.y := F.Y + ph; loc.dx := SU(tw); loc.dy := 0;
  533.         loc.line := t; loc.trunc := FALSE
  534.     END LocateLine;
  535.     PROCEDURE LocateChar* (F: Frame; x, y: INTEGER; VAR loc: Location);
  536.         VAR t: TextLine; pat: Display.Pattern; i: LONGINT; trunc: BOOLEAN;
  537.             n, w, lm, pw, tw, ddx, cn, dx, xc, yc, wc, hc: INTEGER;
  538.     BEGIN LocateLine(F, y, loc); t := loc.line; w := PU(x - F.X); AdjustMetrics(F, t, pw, tw, ddx, cn);
  539.         lm := PU(F.left) + SHORT(P.left DIV Unit);
  540.         IF (t # F.trailer) & (w > pw) THEN Texts.OpenReader(R, F.text, t.org);
  541.             i := 0; n := 0; dx := 0; nextCh := 0X;
  542.             WHILE (i < t.len) & (pw + dx < w) DO Texts.Read(R, nextCh); INC(i); INC(pw, dx);
  543.                 IF nextCh <= " " THEN GetSpecial(F, n, cn, ddx, pw - lm, dx, xc, yc, wc, hc, trunc)
  544.                 ELSE GetChar(R.fnt, nextCh, dx, xc, yc, wc, hc, pat); trunc := FALSE
  545.                 END
  546.             END;
  547.             IF pw + dx < w THEN INC(i); INC(pw, dx); R.elem := NIL END;
  548.             INC(loc.pos, i - 1); loc.x := F.X + SU(pw); loc.trunc := trunc;
  549.             IF i < t.len THEN loc.dx := SU(dx); loc.dy := R.fnt.height * R.voff DIV 64 ELSE loc.dx := 4 END
  550.         ELSE loc.dx := 4
  551.         END
  552.     END LocateChar;
  553.     PROCEDURE LocateWord* (F: Frame; x, y: INTEGER; VAR loc: Location);
  554.         VAR t: TextLine; pos, i: LONGINT; px, rx: INTEGER; pat: Display.Pattern; dx, xc, yc, wc, hc: INTEGER;
  555.     BEGIN LocateChar(F, x, y, loc); pos := loc.pos + 1;
  556.         REPEAT DEC(pos); Texts.OpenReader(R, F.text, pos); Texts.Read(R, nextCh)
  557.         UNTIL (pos < loc.org) OR (nextCh > " ");
  558.         INC(pos);
  559.         REPEAT DEC(pos); Texts.OpenReader(R, F.text, pos); Texts.Read(R, nextCh)
  560.         UNTIL (pos < loc.org) OR (nextCh <= " ");
  561.         LocatePos(F, pos + 1, loc); t := loc.line; i := loc.pos - loc.org;
  562.         IF i < t.len THEN px := PU(loc.x); rx := PU(F.X + F.W - F.right);
  563.             Texts.OpenReader(R, F.text, loc.pos); dx := 0; wc := 0; nextCh := "x";
  564.             WHILE (i < t.len) & (nextCh > " ") & (px + dx < rx) DO Texts.Read(R, nextCh); INC(i); INC(px, dx);
  565.                 GetChar(R.fnt, nextCh, dx, xc, yc, wc, hc, pat)
  566.             END;
  567.             IF (nextCh > " ") & (px + dx < rx) THEN INC(i); INC(px, dx) END;
  568.             loc.dx := SU(px) - loc.x
  569.         ELSE loc.dx := 0
  570.         END
  571.     END LocateWord;
  572.     PROCEDURE Pos* (F: Frame; x, y: INTEGER): LONGINT;
  573.         VAR loc: Location;
  574.     BEGIN LocateChar(F, x, y, loc); RETURN loc.pos
  575.     END Pos;
  576.     PROCEDURE ThisSubFrame* (F: Frame; x, y: INTEGER): Display.Frame;
  577.         VAR f: Display.Frame;
  578.     BEGIN f := F.dsc;
  579.         WHILE (f # NIL) & ((x < f.X) OR (x >= f.X + f.W) OR (y < f.Y) OR (y >= f.Y + f.H)) DO f := f.next END;
  580.         RETURN f
  581.     END ThisSubFrame;
  582.     (** Caret & Selection **)
  583.     PROCEDURE PassSubFocus* (F: Frame; f: Display.Frame);
  584.         VAR loc: Location; f1: Display.Frame; ctrl: Oberon.ControlMsg; focus: TextFrames.FocusMsg;
  585.     BEGIN
  586.         IF F.focus # NIL THEN f1 := F.focus;
  587.             ctrl.id := Oberon.defocus; f1.handle(f1, ctrl);
  588.             LocateChar(F, f1.X + 1, f1.Y + 1, loc);
  589.             focus.focus := FALSE; focus.elemFrame := f1; focus.frame := F; R.elem.handle(R.elem, focus);
  590.             InvertBorder(f1)
  591.         END;
  592.         IF f # NIL THEN
  593.             LocateChar(F, f.X + 1, f.Y + 1, loc);
  594.             focus.focus := TRUE; focus.elemFrame := f; focus.frame := F; R.elem.handle(R.elem, focus);
  595.             InvertBorder(f)
  596.         END;
  597.         F.focus := f
  598.     END PassSubFocus;
  599.     PROCEDURE RemoveSelection* (F: Frame);
  600.     BEGIN
  601.         IF F.hasSel THEN InvertSelection(F, F.SelBeg, F.SelEnd); F.hasSel := FALSE END
  602.     END RemoveSelection;
  603.     PROCEDURE SetSelection* (F: Frame; beg, end: LONGINT);    (**forces range to visible bounds**)
  604.         VAR loc: Location;
  605.     BEGIN
  606.         IF end > F.text.len THEN end := F.text.len END;
  607.         IF end > beg THEN
  608.             IF F.hasSel & (F.SelBeg.pos = beg) THEN
  609.                 IF (F.SelEnd.pos < end) & (F.SelEnd.pos < F.trailer.org) THEN
  610.                     LocatePos(F, F.SelEnd.pos, loc); LocatePos(F, end, F.SelEnd); InvertSelection(F, loc, F.SelEnd)
  611.                 ELSIF end < F.SelEnd.pos THEN
  612.                     LocatePos(F, end, loc); InvertSelection(F, loc, F.SelEnd); LocatePos(F, end, F.SelEnd)
  613.                 END
  614.             ELSIF ~F.hasSel OR (F.SelBeg.pos # beg) OR (F.SelEnd.pos # end) THEN
  615.                 RemoveSelection(F); PassSubFocus(F, NIL);
  616.                 LocatePos(F, beg, F.SelBeg); LocatePos(F, end, F.SelEnd); InvertSelection(F, F.SelBeg, F.SelEnd)
  617.             END;
  618.             F.hasSel := TRUE; F.time := Oberon.Time()
  619.         END
  620.     END SetSelection;
  621.     PROCEDURE RemoveCaret* (F: Frame);
  622.         VAR msg: Oberon.ControlMsg;
  623.     BEGIN
  624.         IF F.focus # NIL THEN msg.id := Oberon.defocus; F.focus.handle(F.focus, msg) END;
  625.         IF F.hasCar THEN InvertCaret(F); F.hasCar := FALSE END
  626.     END RemoveCaret;
  627.     PROCEDURE SetCaret* (F: Frame; pos: LONGINT);    (**only done if within visible bounds**)
  628.     BEGIN
  629.         IF ~F.hasCar OR (F.CarLoc.pos # pos) THEN RemoveCaret(F); PassSubFocus(F, NIL);
  630.             LocatePos(F, pos, F.CarLoc);
  631.             IF (F.H - F.top - F.bot >= F.CarLoc.line.h) & (F.CarLoc.x <= F.X + F.W - F.right) THEN
  632.                 LocateChar(F, F.CarLoc.x + 1, F.CarLoc.y, F.CarLoc); (*prevent "dangling" caret at right margin*)
  633.                 IF F.CarLoc.pos = pos THEN InvertCaret(F); F.hasCar := TRUE END
  634.             END
  635.         END
  636.     END SetCaret;
  637.     PROCEDURE Neutralize* (F: Frame);
  638.         VAR f: Display.Frame; msg: Oberon.ControlMsg;
  639.     BEGIN RemoveCaret(F); RemoveSelection(F);
  640.         f := F.dsc; msg.id := Oberon.neutralize;
  641.         WHILE f # NIL DO f.handle(f, msg);
  642.             IF f = F.focus THEN PassSubFocus(F, NIL) END;
  643.             f := f.next
  644.         END
  645.     END Neutralize;
  646.     (** Display Range **)
  647.     PROCEDURE Complete (F: Frame; trailer: TextLine; VAR s: TextLine; VAR org: LONGINT; VAR ph: INTEGER);
  648.         VAR u: TextLine;
  649.     BEGIN
  650.         IF ph > F.bot THEN    (*try to add new lines to the bottom*)
  651.             Texts.OpenReader(R, F.text, org); Texts.Read(R, nextCh);
  652.             LOOP
  653.                 IF R.eot THEN EXIT END;
  654.                 NEW(u); u.org := org; MeasureLine(F, F.W - F.left - F.right, u);
  655.                 IF ph - u.h < F.bot THEN EXIT END;
  656.                 s.next := u; s := s.next; DEC(ph, s.h); INC(org, s.span)
  657.             END
  658.         END;
  659.         s.next := trailer; trailer.eot := TRUE; trailer.org := org; trailer.len := 0; trailer.w := 0;
  660.         trailer.h := SHORT(TextFrames.defParc.lsp DIV unit); trailer.P := P; trailer.pbeg := pbeg
  661.     END Complete;
  662.     PROCEDURE ShowFrom (F: Frame; pos: LONGINT);    (*removes global marks as needed and neutralizes F*)
  663.         VAR new, s: TextLine; beg, end: Location; org: LONGINT; ph, y0, dy: INTEGER;
  664.     BEGIN Neutralize(F);
  665.         IF (F.trailer # NIL) & (F.org < pos) & (pos < F.trailer.org) THEN    (*shift up and extend to the bottom*)
  666.             LocateLineTop(F, F.trailer, pos, beg); LocateLineTop(F, F.trailer, F.trailer.org, end);
  667.             dy := (F.Y + F.H - F.top) - beg.y; Shift(F, end.y, end.y + dy, beg.y - end.y);
  668.             Erase(F, F.X + F.left, end.y, F.W - F.left, dy);
  669.             s := F.trailer.next; WHILE s.org # pos DO s := s.next END;
  670.             F.trailer.next := s; org := s.org + s.span; ph := F.H - F.top - s.h;
  671.             Complete(F, F.trailer, s, org, ph); F.org := pos; ShowLines(F, F.bot, end.y + dy - F.Y)
  672.         ELSIF (F.trailer = NIL) OR (pos # F.org) THEN MeasureLines(F, pos, new);
  673.             IF (F.trailer # NIL) & (pos < F.org) & (F.org <= new.org) THEN    (*shift down and extend to the top*)
  674.                 LocateLineTop(F, new, F.org, beg); LocateLineTop(F, new, new.org, end);
  675.                 y0 := F.Y + F.H - F.top; Shift(F, y0 - (beg.y - end.y), end.y, beg.y - end.y);
  676.                 Erase(F, F.X + F.left, beg.y, F.W - F.left, y0 - beg.y);
  677.                 Erase(F, F.X + F.left, F.Y + F.bot, F.W - F.left, end.y - (F.Y + F.bot));
  678.                 F.org := pos; F.trailer := new; ShowLines(F, beg.y - F.Y, F.H - F.top)
  679.             ELSE    (*full redisplay*)
  680.                 IF F.trailer = NIL THEN Erase(F, F.X, F.Y, F.W, F.H); ShowBar(F, 0, F.H); F.markH := -1
  681.                 ELSE Erase(F, F.X + F.left, F.Y + F.bot, F.W - F.left, F.H - F.bot - F.top)
  682.                 END;
  683.                 F.org := pos; F.trailer := new; ShowLines(F, F.bot, F.H - F.top)
  684.             END
  685.         END;
  686.         ShowTick(F)
  687.     END ShowFrom;
  688.     PROCEDURE Show* (F: Frame; pos: LONGINT);    (**removes global marks as needed and neutralizes F**)
  689.     BEGIN BegOfLine(F.text, pos, TRUE); ShowFrom(F, pos)
  690.     END Show;
  691.     PROCEDURE Resize* (F: Frame; x, y, w, h: INTEGER);
  692.         VAR loc: Location; oldY, oldH, dh: INTEGER;
  693.     BEGIN
  694.         IF (w = 0) OR (h = 0) THEN InvalSubFrames(F, F.X, F.Y, F.W, F.H);
  695.             F.X := x; F.Y := y; F.W := w; F.H := h; F.trailer := NIL
  696.         ELSIF (F.trailer # NIL) & (x = F.X) & (w = F.W) THEN
  697.             oldY := F.Y; oldH := F.H; Tick(F); F.markH := -1; F.Y := y; F.H := h;
  698.             IF h > oldH THEN dh := h - oldH;
  699.                 Display.CopyBlock(x, oldY, w, oldH, x, y + dh, Display.replace);
  700.                 ShiftSubFrames(F, oldY, y + dh, oldH);
  701.                 EraseRect(F, x, y, w, dh); ShowBar(F, 0, dh);
  702.                 LocateLineTop(F, F.trailer, F.trailer.org, loc); MeasureLines(F, F.org, F.trailer);
  703.                 ShowLines(F, F.bot, loc.y - F.Y)
  704.             ELSE dh := oldH - h;
  705.                 MeasureLines(F, F.org, F.trailer); LocateLineTop(F, F.trailer, F.trailer.org, loc);
  706.                 Display.CopyBlock(x, oldY + dh, w, h, x, y, Display.replace);
  707.                 ShiftSubFrames(F, oldY + dh, y, h);
  708.                 EraseRect(F, x + F.left, y, w - F.left, loc.y - F.Y);
  709.                 InvalSubFrames(F, x, oldY, w, y - oldY); InvalSubFrames(F, x, y + h, w, dh - (y - oldY))
  710.             END;
  711.             ShowTick(F)
  712.         ELSE F.X := x; F.Y := y; F.W := w; F.H := h; F.trailer := NIL; Show(F, F.org)
  713.         END
  714.     END Resize;
  715.     (** Contents Update **)
  716.     PROCEDURE Update* (F: Frame; VAR msg: TextFrames.UpdateMsg);    (**removes global marks as needed**)
  717.         VAR t: TextLine; org, d: LONGINT;
  718.         PROCEDURE Begin (VAR beg: LONGINT; VAR org0: LONGINT; VAR q: TextLine);
  719.             (*returns q # NIL if beg > org0*)
  720.             VAR trailer, t: TextLine; p: LONGINT;
  721.         BEGIN trailer := F.trailer; t := trailer; q := NIL;
  722.             WHILE (t.next # trailer) & (t.next.org + t.next.span <= beg) & ~t.next.eot DO t := t.next END;
  723.             IF (t # trailer) & (t.next # trailer) & (beg <= t.next.org + t.next.span) THEN
  724.                 Texts.OpenReader(R, F.text, t.org); Texts.Read(R, nextCh); p := t.org; NextLine(F.text, p);
  725.                 IF p = t.next.org THEN q := t.next; org0 := q.org ELSE org0 := t.org; beg := org0 END
  726.             ELSE BegOfLine(F.text, beg, TRUE);
  727.                 IF (msg.beg < beg + AdjustSpan) & (F.org < beg) THEN DEC(beg); BegOfLine(F.text, beg, TRUE) END;
  728.                 org0 := beg
  729.             END
  730.         END Begin;
  731.         PROCEDURE Adjust (end, delta: LONGINT);
  732.             VAR new, old, s, t, u, p, q: TextLine; bot: Location;
  733.                 org, org0, beg: LONGINT; ph, h0, h1, H1, h2, lm, dx, dy: INTEGER;
  734.         BEGIN q := NIL; LocateLineTop(F, F.trailer, F.trailer.org, bot);
  735.             IF msg.beg < F.org THEN org0 := F.org; beg := org0 ELSE beg := msg.beg; Begin(beg, org0, q) END;
  736.             NEW(new); s := new; old := F.trailer; t := old; org := F.org; ph := F.H - F.top;
  737.             WHILE (t.next # old) & (t.next.org # org0) DO t := t.next;    (*transfer unchanged prefix*)
  738.                 s.next := t; s := t; DEC(ph, s.h); INC(org, s.span)
  739.             END;
  740.             h0 := ph; H1 := h0; t := t.next; p := s;
  741.             Texts.OpenReader(R, F.text, org); Texts.Read(R, nextCh);    (*rebuilt at least one line descriptor*)
  742.             LOOP NEW(u); u.org := org; MeasureLine(F, F.W - F.left - F.right, u);
  743.                 IF ph - u.h < F.bot THEN h1 := ph; h2 := h1; EXIT END;
  744.                 s.next := u; s := s.next; DEC(ph, s.h); INC(org, s.span);
  745.                 IF R.eot THEN h1 := ph; h2 := h1; EXIT END;
  746.                 IF org > end THEN
  747.                     WHILE (t # old) & (org > t.org + delta) DO DEC(H1, t.h); t := t.next END;
  748.                     IF (org = t.org + delta) & (P = t.P) THEN h1 := ph;    (*resynchronized*)
  749.                         WHILE (t # old) & (ph - t.h >= F.bot) DO    (*transfer unchanged suffix*)
  750.                             s.next := t; s := t; s.org := org; TextFrames.ParcBefore(F.text, s.org, s.P, s.pbeg);
  751.                             DEC(ph, s.h); INC(org, s.span); t := t.next
  752.                         END;
  753.                         h2 := ph; EXIT
  754.                     END
  755.                 END
  756.             END;
  757.             Shift(F, F.Y + h2 + (H1 - h1), F.Y + h2, h1 - h2);
  758.             Complete(F, new, s, org, ph); F.trailer := new; t := p.next;
  759.             IF (q # NIL) & (q.h = t.h) & (q.dsr = t.dsr) & (q.org = t.org) & (q.P = t.P) & (end <= t.org + t.len) THEN
  760.                 P := t.P; pbeg := t.pbeg;
  761.                 IF (P.opts * AdjMask = {leftAdj}) OR (P.opts * AdjMask = AdjMask) & (q.nob = 0) & (t.nob = 0) THEN
  762.                     Width(F, t, beg, lm, dx, dy);    (*preserve prefix of first affected line*)
  763.                     DEC(h0, t.h); Erase(F, F.X + SU(lm), F.Y + h0, F.W - SU(lm), t.h);
  764.                     ShowLine(F, t, PU(F.X) + lm, PU(F.X + F.W - F.right), F.Y + h0)
  765.                 END
  766.             END;
  767.             ShowLinesErasing(F, h1, h0);
  768.             Erase(F, F.X + F.left, bot.y, F.W - F.left, h2 - (bot.y - F.Y)); ShowLines(F, F.bot, h2)
  769.         END Adjust;
  770.     BEGIN
  771.         IF (msg.id = Texts.insert) & (msg.beg < F.org) THEN t := F.trailer; d := msg.end - msg.beg; INC(F.org, d);
  772.             REPEAT INC(t.org, d); t := t.next UNTIL t = F.trailer
  773.         ELSIF (msg.id = Texts.delete) & (msg.end <= F.org) THEN t := F.trailer; d := msg.end - msg.beg; DEC(F.org, d);
  774.             REPEAT DEC(t.org, d); t := t.next UNTIL t = F.trailer
  775.         END;
  776.         org := F.org;
  777.         IF msg.beg <= F.org + AdjustSpan THEN BegOfLine(F.text, org, TRUE) END;
  778.         TextFrames.ParcBefore(F.text, org, P, d);
  779.         IF (org # F.org) OR (P # F.trailer.next.P) OR (F.pages # 0) THEN
  780.             F.trailer := NIL; F.pages := 0; Show(F, F.org)
  781.         ELSIF (msg.end > org) & (msg.beg < F.trailer.org + AdjustSpan) THEN
  782.             IF msg.id = Texts.replace THEN Adjust(msg.end, 0)
  783.             ELSIF msg.id = Texts.insert THEN Adjust(msg.end, msg.end - msg.beg)
  784.             ELSIF msg.id = Texts.delete THEN Adjust(msg.beg, msg.beg - msg.end)
  785.             END
  786.         END;
  787.         ShowTick(F)
  788.     END Update;
  789.     (** User Interface **)
  790.     PROCEDURE TrackLine* (F: Frame; VAR x, y: INTEGER; VAR org: LONGINT; VAR keysum: SET);
  791.         VAR keys: SET; new, old: Location;
  792.     BEGIN LocateLine(F, y, old); InvertRect(F, old.x, old.y, old.dx + 4, 2);
  793.         REPEAT TrackMouse(x, y, keys, keysum); LocateLine(F, y, new);
  794.             IF new.org # old.org THEN
  795.                 InvertRect(F, new.x, new.y, new.dx + 4, 2); InvertRect(F, old.x, old.y, old.dx + 4, 2); old := new
  796.             END
  797.         UNTIL keys = {};
  798.         InvertRect(F, new.x, new.y, new.dx + 4, 2); org := new.org
  799.     END TrackLine;
  800.     PROCEDURE TrackWord* (F: Frame; VAR x, y: INTEGER; VAR pos: LONGINT; VAR keysum: SET);
  801.         VAR keys: SET; new, old: Location;
  802.     BEGIN LocateWord(F, x, y, old); InvertRect(F, old.x, old.y, old.dx, 2);
  803.         REPEAT TrackMouse(x, y, keys, keysum); LocateWord(F, x, y, new);
  804.             IF new.pos # old.pos THEN
  805.                 InvertRect(F, new.x, new.y, new.dx, 2); InvertRect(F, old.x, old.y, old.dx, 2); old := new
  806.             END
  807.         UNTIL keys = {};
  808.         InvertRect(F, new.x, new.y, new.dx, 2); pos := new.pos
  809.     END TrackWord;
  810.     PROCEDURE TrackCaret* (F: Frame; VAR x, y: INTEGER; VAR keysum: SET);
  811.         VAR keys: SET;
  812.     BEGIN
  813.         REPEAT TrackMouse(x, y, keys, keysum); SetCaret(F, Pos(F, x, y)) UNTIL keys = {}
  814.     END TrackCaret;
  815.     PROCEDURE TrackSelection* (F: Frame; VAR x, y: INTEGER; VAR keysum: SET);
  816.         VAR keys: SET; pos: LONGINT; V: Viewers.Viewer; f: Frame;
  817.     BEGIN V := Viewers.This(F.X, F.Y); V := V.next(Viewers.Viewer);
  818.         IF (V.dsc # NIL) & (V.dsc.next # NIL) & (V.dsc.next IS Frame) THEN f := V.dsc.next(Frame);
  819.             IF f.hasSel & (f.text = F.text) THEN
  820.                 IF (f.SelBeg.pos < f.trailer.org) & (f.org < f.SelEnd.pos) & (f.SelBeg.pos <= Pos(F, x, y)) THEN
  821.                     SetSelection(F, f.SelBeg.pos, Pos(F, x, y) + 1)
  822.                 ELSE RemoveSelection(f); f := NIL
  823.                 END
  824.             ELSE f := NIL
  825.             END
  826.         ELSE f := NIL
  827.         END;
  828.         IF f = NIL THEN
  829.             IF F.hasSel & (F.SelBeg.pos + 1 = F.SelEnd.pos) & (Pos(F, x, y) = F.SelBeg.pos) THEN
  830.                 SetSelection(F, F.SelBeg.org, Pos(F, x, y) + 1)
  831.             ELSE SetSelection(F, Pos(F, x, y), Pos(F, x, y) + 1)
  832.             END
  833.         END;
  834.         REPEAT TrackMouse(x, y, keys, keysum); pos := Pos(F, x, y) + 1;
  835.             IF F.hasSel THEN
  836.                 IF pos > F.SelBeg.pos THEN SetSelection(F, F.SelBeg.pos, pos);
  837.                     IF f # NIL THEN SetSelection(f, f.SelBeg.pos, pos); f.SelEnd.pos := F.SelEnd.pos END
  838.                 END
  839.             ELSE SetSelection(F, Pos(F, x, y), Pos(F, x, y) + 1)
  840.             END
  841.         UNTIL keys = {};
  842.         IF f # NIL THEN F.SelBeg.pos := f.SelBeg.pos END
  843.     END TrackSelection;
  844.     PROCEDURE Call* (F: Frame; pos: LONGINT; new: BOOLEAN);
  845.         VAR S: Texts.Scanner; par: Oberon.ParList; res: INTEGER;
  846.     BEGIN Texts.OpenScanner(S, F.text, pos); Texts.Scan(S);
  847.         IF (S.line = 0) & (S.class = Texts.Name) THEN NEW(par); par.frame := F; par.text := F.text; par.pos := Texts.Pos(S)-1;
  848.             Oberon.Call(S.s, par, new, res);
  849.             IF res > 1 THEN Texts.WriteString(WL, "Call error: "); Texts.WriteString(WL, Modules.importing);
  850.                 IF res = 2 THEN Texts.WriteString(WL, " not an obj-file")
  851.                 ELSIF res = 3 THEN Texts.WriteString(WL, " imports ");
  852.                     Texts.WriteString(WL, Modules.imported); Texts.WriteString(WL, " with bad key")
  853.                 ELSIF res = 4 THEN Texts.WriteString(WL, " corrupted obj file")
  854.                 ELSIF res = 6 THEN Texts.WriteString(WL, " has too many imports")
  855.                 ELSIF res = 7 THEN Texts.WriteString(WL, " not enough space")
  856.                 END;
  857.                 Texts.WriteLn(WL); Texts.Append(Oberon.Log, WL.buf)
  858.             END
  859.         END
  860.     END Call;
  861.     PROCEDURE ShiftBlock (F: Frame; delta: INTEGER);
  862.         VAR text: Texts.Text; pos, beg, end, time: LONGINT; select: SelectMsg; ch: CHAR;
  863.     BEGIN Oberon.GetSelection(text, beg, end, time);
  864.         IF (time >= 0) & (text = F.text) THEN BegOfLine(F.text, beg, FALSE); pos := beg;
  865.             WHILE pos < end DO Texts.OpenReader(R, F.text, pos); Texts.Read(R, ch);
  866.                 WHILE (R.elem # NIL) & (R.elem IS TextFrames.Parc) & (pos < end) DO Texts.Read(R, ch); INC(pos) END;
  867.                 IF pos < end THEN
  868.                     IF delta < 0 THEN
  869.                         IF (ch <= " ") & (ch # CR) & (ch # Texts.ElemChar) THEN
  870.                             Texts.Delete(F.text, pos, pos + 1); DEC(end)
  871.                         END
  872.                     ELSE
  873.                         IF (ch <= " ") & (ch # CR) & (ch # Texts.ElemChar) THEN Texts.Write(W, ch)    (*first char extension*)
  874.                         ELSE Texts.Write(W, TAB)
  875.                         END;
  876.                         Texts.Insert(F.text, pos, W.buf); INC(end); INC(pos)
  877.                     END;
  878.                     Texts.OpenReader(R, F.text, pos);
  879.                     REPEAT Texts.Read(R, ch) UNTIL R.eot OR (ch = CR);
  880.                     pos := Texts.Pos(R)
  881.                 END
  882.             END;
  883.             select.text := F.text; select.beg := beg; select.end := pos; select.time := Oberon.Time();
  884.             Viewers.Broadcast(select)
  885.         END
  886.     END ShiftBlock;
  887.     PROCEDURE Write* (F: Frame; ch: CHAR; fnt: Fonts.Font; col, voff: SHORTINT);
  888.         VAR loc: Location; parc: TextFrames.Parc; org, pos, pbeg: LONGINT; i: INTEGER; ch0: CHAR;
  889.             buf: ARRAY 32 OF CHAR;
  890.             copy: Texts.CopyMsg; input: Oberon.InputMsg;
  891.         PROCEDURE Visible(ch: CHAR): BOOLEAN;
  892.             VAR pat: Display.Pattern; dx, x, y, w, h: INTEGER;
  893.         BEGIN GetChar(W.fnt, ch, dx, x, y, w, h, pat); RETURN dx > 0
  894.         END Visible;
  895.         PROCEDURE InsertBuffer;
  896.             VAR i, j: INTEGER; ch: CHAR;
  897.         BEGIN i := 0; j := 0; ch := buf[i];
  898.             WHILE ch # 0X DO
  899.                 IF (ch = TAB) OR (ch = CR) OR (ch = " ") OR Visible(ch) THEN Texts.Write(W, ch); INC(j) END;
  900.                 INC(i); ch := buf[i]
  901.             END; 
  902.             IF j > 0 THEN Texts.Insert(F.text, pos, W.buf); INC(pos, LONG(j)) END
  903.         END InsertBuffer;
  904.         PROCEDURE Flush;
  905.             VAR ch: CHAR;
  906.         BEGIN
  907.             WHILE Input.Available() > 0 DO Input.Read(ch) END
  908.         END Flush;
  909.     BEGIN
  910.         IF F.hasCar THEN pos := F.CarLoc.pos;
  911.             IF (ch = DEL) & (pos > F.org) THEN DEC(pos); Texts.Delete(F.text, pos, pos + 1); Flush
  912.             ELSIF (ch = CRSL) & (pos > 0) THEN DEC(pos)
  913.             ELSIF (ch = CRSR) & (pos < F.text.len) THEN INC(pos)
  914.             ELSIF (ch = BRK) OR (ch = ShiftBRK) THEN
  915.                 TextFrames.ParcBefore(F.text, pos, P, pbeg); P.handle(P, copy); parc := copy.e(TextFrames.Parc);
  916.                 IF ch = BRK THEN EXCL(parc.opts, pageBreak) ELSE INCL(parc.opts, pageBreak) END;
  917.                 Texts.SetFont(W, fnt); Texts.SetColor(W, col); Texts.SetOffset(W, voff);
  918.                 Texts.WriteElem(W, parc); Texts.Insert(F.text, pos, W.buf); INC(pos)
  919.             ELSIF (ch = TAB) OR (ch = LF) OR (ch = CR) OR (ch >= " ") THEN
  920.                 IF F.text.len > 0 THEN
  921.                     IF pos < F.text.len THEN Texts.OpenReader(R, F.text, pos); Texts.Read(R, ch0) END;
  922.                     IF (pos > 0) & ((pos = F.text.len) OR (ch0 <= " ")) THEN
  923.                         Texts.OpenReader(R, F.text, pos - 1); Texts.Read(R, ch0)
  924.                     END;
  925.                     Texts.SetFont(W, R.fnt); Texts.SetColor(W, R.col);
  926.                     IF (ch = CR) OR (ch = TAB) OR (ch = LF) THEN Texts.SetOffset(W, voff) ELSE Texts.SetOffset(W, R.voff) END
  927.                 ELSE Texts.SetFont(W, fnt); Texts.SetColor(W, col); Texts.SetOffset(W, voff)
  928.                 END;
  929.                 IF ch = LF THEN buf[0] := CR; i := 1; org := F.CarLoc.org; BegOfLine(F.text, org, FALSE);
  930.                     Texts.OpenReader(R, F.text, org);
  931.                     REPEAT Texts.Read(R, ch) UNTIL (R.elem = NIL) OR ~(R.elem IS TextFrames.Parc);
  932.                     WHILE (Texts.Pos(R) <= pos) & (ch <= " ") & (ch # Texts.ElemChar) & (i < 31) DO
  933.                         buf[i] := ch; INC(i); Texts.Read(R, ch)
  934.                     END
  935.                 ELSE buf[0] := ch; i := 1
  936.                 END;
  937.                 WHILE (Input.Available() > 0) & (i < 31) & (ch >= " ") & (ch < DEL) DO Input.Read(buf[i]); INC(i) END;
  938.                 buf[i] := 0X; InsertBuffer
  939.             END;
  940.             IF pos < F.org THEN Show(F, F.org - 1)
  941.             ELSIF pos < F.text.len THEN org := -1;
  942.                 WHILE (pos >= F.trailer.org) & (F.org # org) DO Show(F, F.trailer.next.next.org); Flush; org := F.org END
  943.             ELSE LocatePos(F, pos, loc); LocateChar(F, loc.x + 1, loc.y, loc);
  944.                 IF pos # loc.pos THEN Show(F, F.trailer.next.next.org); Flush END
  945.             END;
  946.             SetCaret(F, pos)
  947.         ELSIF F.focus # NIL THEN input.id := Oberon.consume; input.ch := ch;
  948.             input.fnt := fnt; input.col := col; input.voff := voff; F.focus.handle(F.focus, input)
  949.         ELSIF F.hasSel THEN
  950.             IF ch = CRSL THEN ShiftBlock(F, -1); Flush ELSIF ch = CRSR THEN ShiftBlock(F, 1); Flush END
  951.         END
  952.     END Write;
  953.     PROCEDURE TouchElem* (F: Frame; VAR x, y: INTEGER; VAR keysum: SET);
  954.         VAR loc: Location; e: Texts.Elem; pbeg: LONGINT; y0: INTEGER;
  955.             track: TextFrames.TrackMsg;
  956.     BEGIN LocateChar(F, x, y, loc); e := R.elem;
  957.         IF (e # NIL) & (loc.x + e.W DIV unit <= F.X + F.W - F.right) THEN
  958.             TextFrames.ParcBefore(F.text, loc.pos, P, pbeg);
  959.             y0 := loc.y + loc.line.dsr - SHORT(P.dsr DIV unit) + loc.dy;
  960.             IF (loc.x <= x) & (x < loc.x + e.W DIV unit) & ~loc.trunc THEN
  961.                 track.X := x; track.Y := y; track.keys := keysum;
  962.                 track.fnt := R.fnt; track.col := R.col; track.pos := Texts.Pos(R) - 1;
  963.                 track.frame := F; track.X0 := loc.x; track.Y0 := y0;
  964.                 e.handle(e, track); Input.Mouse(keysum, x, y)
  965.             END
  966.         END
  967.     END TouchElem;
  968.     PROCEDURE Edit* (F: Frame; x, y: INTEGER; keysum: SET);
  969.         VAR ef: Display.Frame; text: Texts.Text; beg, end, time, pos: LONGINT; keys: SET; ch: CHAR;
  970.             copyover: Oberon.CopyOverMsg; input: Oberon.InputMsg;
  971.     BEGIN
  972.         IF x < F.X + F.barW THEN    (*scroll bar*)
  973.             IF leftKey IN keysum THEN TrackLine(F, x, y, pos, keysum)
  974.             ELSIF middleKey IN keysum THEN
  975.                 REPEAT TrackMouse(x, y, keys, keysum) UNTIL keys = {};
  976.                 IF keysum = {middleKey, leftKey} THEN pos := F.text.len; BegOfLine(F.text, pos, TRUE)
  977.                 ELSIF keysum = {middleKey, rightKey} THEN pos := 0
  978.                 ELSIF (F.Y <= y) & (y <= F.Y + F.H) THEN pos := CoordToPos(F, y - F.Y); BegOfLine(F.text, pos, TRUE)
  979.                 ELSE pos := F.org
  980.                 END
  981.             ELSIF rightKey IN keysum THEN
  982.                 REPEAT TrackMouse(x, y, keys, keysum) UNTIL keys = {};
  983.                 pos := 0
  984.             ELSE DrawCursor(x, y)
  985.             END;
  986.             IF (keysum # {}) & (keysum # cancel) THEN ShowFrom(F, pos) END
  987.         ELSE    (*text area*)
  988.             ef := ThisSubFrame(F, x, y);
  989.             IF ef # NIL THEN    (*within sub-frame*)
  990.                 IF (F.focus # ef) & (keysum = {leftKey}) THEN
  991.                     REPEAT TrackMouse(x, y, keys, keysum) UNTIL keys = {};
  992.                     IF keysum = {leftKey} THEN RemoveSelection(F); RemoveCaret(F); PassSubFocus(F, ef); RETURN
  993.                     END
  994.                 ELSIF F.focus = ef THEN input.id := Oberon.track; input.keys := keysum; input.X := x; input.Y := y;
  995.                     ef.handle(ef, input); RETURN
  996.                 END
  997.             END;
  998.             IF keysum # {} THEN TouchElem(F, x, y, keysum) END;
  999.             IF leftKey IN keysum THEN Oberon.PassFocus(Viewers.This(F.X, F.Y)); TrackCaret(F, x, y, keysum);
  1000.                 IF (keysum = {leftKey, middleKey}) & F.hasCar THEN Oberon.GetSelection(text, beg, end, time);
  1001.                     IF time >= 0 THEN Texts.Save(text, beg, end, B);
  1002.                         Texts.Insert(F.text, F.CarLoc.pos, B); SetCaret(F, F.CarLoc.pos + (end - beg))
  1003.                     END
  1004.                 ELSIF (keysum = {leftKey, rightKey}) & F.hasCar & (F.CarLoc.pos < F.text.len) THEN
  1005.                     Oberon.GetSelection(text, beg, end, time);
  1006.                     IF time >= 0 THEN Texts.OpenReader(R, F.text, F.CarLoc.pos); Texts.Read(R, ch);
  1007.                         Texts.ChangeLooks(text, beg, end, {0, 1, 2}, R.fnt, R.col, R.voff)
  1008.                     END
  1009.                 END
  1010.             ELSIF middleKey IN keysum THEN TrackWord(F, x, y, pos, keysum);
  1011.                 IF keysum # cancel THEN Call(F, pos, keysum = {middleKey, leftKey}) END
  1012.             ELSIF rightKey IN keysum THEN TrackSelection(F, x, y, keysum);
  1013.                 IF keysum = {rightKey, middleKey} THEN
  1014.                     copyover.text := F.text; copyover.beg := F.SelBeg.pos; copyover.end := F.SelEnd.pos;
  1015.                     Oberon.FocusViewer.handle(Oberon.FocusViewer, copyover)
  1016.                 ELSIF (keysum = {rightKey, leftKey}) & F.hasSel THEN Oberon.PassFocus(Viewers.This(F.X, F.Y));
  1017.                     Texts.Delete(F.text, F.SelBeg.pos, F.SelEnd.pos); SetCaret(F, F.SelBeg.pos)
  1018.                 END
  1019.             ELSE DrawCursor(x, y)
  1020.             END
  1021.         END
  1022.     END Edit;
  1023.     (** General **)
  1024.     PROCEDURE NotifyElems* (F: Frame; VAR msg: Display.FrameMsg);
  1025.         VAR p, f: Display.Frame;
  1026.     BEGIN f := F.dsc;
  1027.         IF msg IS TextFrames.NotifyMsg THEN msg(TextFrames.NotifyMsg).frame := F END;
  1028.         WHILE f # NIL DO p := f; f := f.next; p.handle(p, msg) END
  1029.     END NotifyElems;
  1030.     PROCEDURE Copy* (SF, DF: Frame);
  1031.         VAR i: INTEGER;
  1032.     BEGIN (*TextFrames.Copy(SF, DF)*)
  1033.         DF.handle := SF.handle; DF.text := SF.text; DF.org := SF.org;
  1034.         DF.left := SF.left; DF.right := SF.right; DF.top := SF.top; DF.bot := SF.bot;
  1035.         DF.barW := SF.barW; DF.hasCar := FALSE; DF.hasSel := FALSE;
  1036.         DF.trailer := NIL; DF.pages := SF.pages; DF.first := SF.first; DF.width := SF.width;
  1037.         i := SF.pages;
  1038.         WHILE i > 0 DO DEC(i); DF.porg[i] := SF.porg[i] END
  1039.     END Copy;
  1040.     PROCEDURE Open* (F: Frame; T: Texts.Text; pos: LONGINT);
  1041.     BEGIN TextFrames.Open(F, T, pos);
  1042.         F.trailer := NIL; F.pages := 0
  1043.     END Open;
  1044.     PROCEDURE Handle* (f: Display.Frame; VAR msg: Display.FrameMsg);
  1045.         VAR F, F1: Frame;
  1046.     BEGIN F := f(Frame);
  1047.         IF msg IS Oberon.InputMsg THEN
  1048.             WITH msg: Oberon.InputMsg DO
  1049.                 IF msg.id = Oberon.consume THEN Write(F, msg.ch, msg.fnt, msg.col, msg.voff)
  1050.                 ELSIF msg.id = Oberon.track THEN Edit(F, msg.X, msg.Y, msg.keys)
  1051.                 END
  1052.             END
  1053.         ELSIF msg IS Oberon.ControlMsg THEN NotifyElems(F, msg);
  1054.             WITH msg: Oberon.ControlMsg DO
  1055.                 IF msg.id = Oberon.defocus THEN RemoveCaret(F)
  1056.                 ELSIF msg.id = Oberon.neutralize THEN Neutralize(F)
  1057.                 END
  1058.             END
  1059.         ELSIF msg IS Oberon.CopyMsg THEN NEW(F1); Copy(F, F1); msg(Oberon.CopyMsg).F := F1
  1060.         ELSIF msg IS TextFrames.UpdateMsg THEN NotifyElems(F, msg);
  1061.             WITH msg: TextFrames.UpdateMsg DO
  1062.                 IF msg.text = F.text THEN MarkMenu(F); Neutralize(F); Update(F, msg) END
  1063.             END
  1064.         ELSIF msg IS Oberon.SelectionMsg THEN NotifyElems(F, msg);
  1065.             WITH msg: Oberon.SelectionMsg DO
  1066.                 IF F.hasSel & (F.time > msg.time) THEN
  1067.                     msg.text := F.text; msg.beg := F.SelBeg.pos; msg.end := F.SelEnd.pos; msg.time := F.time
  1068.                 END
  1069.             END
  1070.         ELSIF msg IS Oberon.CopyOverMsg THEN NotifyElems(F, msg);
  1071.             WITH msg: Oberon.CopyOverMsg DO
  1072.                 IF F.hasCar THEN Texts.Save(msg.text, msg.beg, msg.end, B);
  1073.                     Texts.Insert(F.text, F.CarLoc.pos, B); SetCaret(F, F.CarLoc.pos + (msg.end - msg.beg))
  1074.                 END
  1075.             END
  1076.         ELSIF msg IS MenuViewers.ModifyMsg THEN
  1077.             WITH msg: MenuViewers.ModifyMsg DO Neutralize(F); Resize(F, F.X, msg.Y, F.W, msg.H) END
  1078.         ELSIF msg IS SelectMsg THEN NotifyElems(F, msg);
  1079.             WITH msg: SelectMsg DO
  1080.                 IF (msg.text = F.text) & ~F.hasSel THEN Oberon.RemoveMarks(F.X, F.Y, F.W, F.H); Neutralize(F);
  1081.                     SetSelection(F, msg.beg, msg.end); F.time := msg.time;
  1082.                     IF F.hasSel THEN F.SelBeg.pos := msg.beg; F.SelEnd.pos := msg.end END
  1083.                 END
  1084.             END
  1085.         ELSE NotifyElems(F, msg)
  1086.         END
  1087.     END Handle;
  1088.     PROCEDURE NewText* (T: Texts.Text; pos: LONGINT): Frame;
  1089.         VAR frame: Frame;
  1090.     BEGIN NEW(frame);
  1091.         TextFrames.Open(frame, T, pos);
  1092.         frame.handle := Handle;
  1093.         RETURN frame
  1094.     END NewText;
  1095. BEGIN Texts.OpenWriter(W); Texts.OpenWriter(WL);
  1096.     NEW(PB); Texts.OpenBuf(PB); NEW(B); Texts.OpenBuf(B);
  1097.     pfnt := Fonts.This("Syntax8.Scn.Fnt");
  1098.     TextPrinter.InitFonts
  1099. END TextPFrames.
  1100.